---
sidebar_position: 3
---

# IExcelCellReader

:::note
之前的 `IExcelRead` 只能做读取操作，并且会将所有数据都读为 `string`

但是这种操作并不适合所有的使用场景

在我整合导出相关功能的时候就发现了不足

于是为了统一导出操作，做了 `IExcelCellReader`
:::

## 接口定义

:::info
`IExcelCellReader` 相关的接口定义类似下面这样
:::

```csharp
public interface IExcelCellReader : IDisposable
{
    IEnumerable<IReadCell> this[string field] { get; }
    IEnumerable<IReadCell> this[long row] { get; }
    IReadCell this[long row, long col] { get; }
    IReadCell this[string field, long row] { get; }
    IEnumerable<string> Headers { get; }
    IDictionary<string, int> HeadersWithIndex { get; }
    long RowCount { get; }
}
public interface IReadCell
{
    int Row { get; }
    int Col { get; }
    string StringValue { get; }
    Type ValueType { get; }
    object Value { get; set; }
}
public interface IReadCell<T> : IReadCell
{
    T Cell { get; }
}
```

:::tip
与之前的 `IExcelRead` 类似，但是不会再将索引器的返回值强制转为 `string`

`IReadCell` 会提供更多的单元格信息

也能支持后续更多的扩展
:::

## 获取 IExcelCellReader 实例

:::note
现阶段拥有 `NPOICellRead EPPlusCellRead MiniCellRead` 三种实现
:::

```csharp
using IExcelCellReader reader = new NPOICellRead(filePath);
using IExcelCellReader reader = new EPPlusCellRead(filePath);
using IExcelCellReader reader = new MiniCellRead(filePath);
```

## 简单使用

### 获取单元格

```csharp
// 第 1 行 第 6 列
IReadCell value = reader[0, 5];
```

### 获取行

```csharp
IEnumerable<IReadCell> row = reader[0];
```

### 获取列

:::info
我认为"列"与"行"不同, 是不适合直接用下标去获取的, 所以设计成传有"含义"的列名称
:::

```csharp
IEnumerable<IReadCell> col = reader["Column"];
```

### 设置值

```csharp
reader[1, 0].Value = 2333;
reader[1, 1].Value = "9999";
```

:::note
暂时只有单元格的 `Value` 支持修改，其他的都只实现了 `get`
:::

## 保存修改

对单元格的 `Value` 进行修改之后，可以使用 `reader.Save()` 进行保存

```csharp
reader[1, 0].Value = 2333;
reader[1, 1].Value = "9999";
// 保存到打开这个reader时所使用的文件或者流
reader.Save();
// 保存到指定路劲
reader.SaveTo(path);
// 保存到指定的流
reader.SaveTo(stream);
// 直接获取一个新的流
using var streamFromReader =  reader.GetStream();
```

:::tip
直接使用 Save() 会将修改保存到原来的文件或者流中

如果不想影响到原文件，可以使用 SaveTo 的操作保存到新的文件中去

这些保存都是覆盖式保存，无论指定的文件是否有内容，都会将其清空然后覆盖
:::

## 没有原文件?

:::tip
没有文件或者流也可以创建 `reader`，现在的实现会以一个空的流创建空的 `reader`
:::

```csharp
using var reader = new NPOICellRead();
reader[1, 0].Value = 2333;
reader[1, 1].Value = "9999";
reader.SaveTo(path);
reader.SaveTo(stream);
```

:::note
由于没有实际文件或者流的支撑，所以直接使用 Save()将无事发生
:::

## 实现了 IEnumerable

:::note
`IExcelCellReader` 和 `IExcelRead` 一样都实现了 IEnumerable 接口
:::

```csharp
IExcelCellReader reader = new MiniCellRead(path);
reader.Count();
var IEnumerable<IReadCell> firstData = reader.First();
var IEnumerable<IReadCell> lastData = reader.Last();
var IEnumerable<IReadCell> skipOne = reader.Skip(1).First()
```

:::tip
由于实现了 `IEnumerable`，所以也可以使用一些 `Collapsenav.Net.Tool` 的方法
:::

```csharp
IExcelCellReader reader = new MiniCellRead(path);
IEnumerable<IReadCell> mergeData = reader.Merge()
```

:::caution
由于实现问题，有些操作无法使用，比如 `AddRange` 之类存在修改操作的方法

后面可能会做这些方便的实现
:::
